/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-12-5      SummerGift   first version
 */

#include "board.h"
#define PKG_USING_FAL
#ifdef BSP_USING_ON_CHIP_FLASH
#if defined(PKG_USING_FAL)
#include "fal.h"
#include "fal_cfg.h"
#include "board.h"
#endif

//#define DRV_DEBUG
#define LOG_TAG                "drv.flash"
#include <rtdbg.h>
//#define HC32_FLASH_START_ADRESS_08K 0
//#define FLASH_SIZE_GRANULARITY_08K
/**
  * @brief  Gets the block of a given address
  * @param  None
  * @retval The sector of a given address
  */
static rt_uint32_t GetBlock(rt_uint32_t Address)
{
    rt_uint32_t sector = 0;
    sector = Address/(1024*1024);
    return sector;
}


/**
  * @brief  Gets the sector of a given address
  * @param  None
  * @retval The sector of a given address
  */
static rt_uint32_t GetSector(rt_uint32_t Address)
{
    rt_uint32_t sector = 0;
    sector = Address/(8*1024);

    return sector;
}


/**
 * Read data from flash.
 * @note This operation's units is word.
 *
 * @param addr flash address
 * @param buf buffer to store read data
 * @param size read bytes size
 *
 * @return result
 */
int hc32_flash_read(rt_uint32_t addr, rt_uint8_t *buf, size_t size)
{
    size_t i;

    if ((addr + size) > HC32_FLASH_END_ADDRESS)
    {
        LOG_E("read outrange flash size! addr is (0x%p)", (void*)(addr + size));
        return -1;
    }

    for (i = 0; i < size; i++, buf++, addr++)
    {
        *buf = *(rt_uint8_t *) addr;
    }

    return size;
}

/**
 * Write data to flash.
 * @note This operation's units is word.
 * @note This operation must after erase. @see flash_erase.
 *
 * @param addr flash address
 * @param buf the write data buffer
 * @param size write bytes size
 *
 * @return result
 */
int hc32_flash_write(rt_uint32_t addr, const rt_uint8_t *buf, size_t size)
{
    rt_err_t result      = RT_EOK;
    rt_uint32_t end_addr = addr + size;

    if ((end_addr) > HC32_FLASH_END_ADDRESS)
    {
        LOG_E("write outrange flash size! addr is (0x%p)", (void*)(addr + size));
        return -RT_EINVAL;
    }

    if (size < 1)
    {
        return -RT_EINVAL;
    }

    /* Register write enable for some required peripherals. */
    LL_PERIPH_WE(LL_PERIPH_EFM);
    en_flag_status_t flag ;


    int block_no = GetBlock(addr);
    /* Wait flash0, flash1 ready. */
    do {
        if(block_no)
            flag = EFM_GetStatus(EFM_FLAG_RDY1);
        else
            flag = EFM_GetStatus(EFM_FLAG_RDY);
    } while (SET != flag);

    /* EFM_FWMC wirte enable */
    EFM_FWMC_Cmd(ENABLE);
    /* Sector disables write protection */
    /* Get the 1st sector to erase */
    rt_uint32_t FirstSector = GetSector(addr);
    /* Get the number of sector to erase from 1st sector*/
    rt_uint32_t  NbOfSectors = GetSector(addr + size - 1) - FirstSector + 1;         
    EFM_SequenceSectorOperateCmd(FirstSector,NbOfSectors,ENABLE);
    /* Single program */
    
    
    for(int i=0; i<size; i+=4)
    {
        if (LL_OK == EFM_ProgramWord(addr, *(unsigned int*)&buf[i]))
        {
            
            if(*(unsigned int*)&buf[i] != *(unsigned int *) addr)
            {
                rt_kprintf("write err addr %d *(unsigned int *) addr %d  block_no %d\n",addr,*(unsigned int *) addr, block_no);
            }
            addr+=4;
        }
        else
        {      
                rt_kprintf("write flash err addr %d *(unsigned int *) addr %d\n",addr,*(unsigned int *) addr);
            return -RT_ERROR;
        }
    }

    /* Sector 10 Enable write protection */
    EFM_SequenceSectorOperateCmd(FirstSector,NbOfSectors,DISABLE);
    EFM_FWMC_Cmd(DISABLE);
    /* Register write protected for some required peripherals. */
    LL_PERIPH_WP(LL_PERIPH_EFM);

    if (result != RT_EOK)
    {
        return result;
    }

    return size;
}

/**
 * Erase data on flash.
 * @note This operation is irreversible.
 * @note This operation's units is different which on many chips.
 *
 * @param addr flash address
 * @param size erase bytes size
 *
 * @return result
 */
int hc32_flash_erase(rt_uint32_t addr, size_t size)
{
    rt_err_t result = RT_EOK;
//    rt_uint32_t FirstSector = 0, NbOfSectors = 0;
//    rt_uint32_t SECTORError = 0;

    if ((addr + size) > HC32_FLASH_END_ADDRESS)
    {
        LOG_E("ERROR: erase outrange flash size! addr is (0x%p)\n", (void*)(addr + size));
        return -RT_EINVAL;
    }

    /* Register write enable for some required peripherals. */
    LL_PERIPH_WE(LL_PERIPH_EFM);
    en_flag_status_t flag = 0;


    int block_no = GetBlock(addr);
    /* Wait flash0, flash1 ready. */
    do {
        if(block_no)
            flag = EFM_GetStatus(EFM_FLAG_RDY1);
        else
            flag = EFM_GetStatus(EFM_FLAG_RDY);
    } while (SET != flag);

    /* EFM_FWMC wirte enable */
    EFM_FWMC_Cmd(ENABLE);  
    /* Get the 1st sector to erase */
    rt_uint32_t FirstSector = GetSector(addr);
    /* Get the number of sector to erase from 1st sector*/
    rt_uint32_t  NbOfSectors = GetSector(addr + size - 1) - FirstSector + 1;         
    EFM_SequenceSectorOperateCmd(FirstSector,NbOfSectors,ENABLE);
    /* Erase sector 62. */
    for(int i = 0u; i < 256u; i++)
    {
        if(i*8*1024>=size)
            break;
        if(LL_OK != EFM_SectorErase(addr+i*8*1024))
        {
            result = -RT_ERROR;
            break;
        }
    }
    /* Lock EFM. */
    /* Sector 10 Enable write protection */
    EFM_SequenceSectorOperateCmd(FirstSector,NbOfSectors,DISABLE);
    EFM_FWMC_Cmd(DISABLE);
    /* Register write protected for some required peripherals. */
    LL_PERIPH_WP(LL_PERIPH_EFM);

    EFM_ClearStatus(EFM_FLAG_ALL);
    if (result != RT_EOK)
    {
        return result;
    }

    LOG_D("erase done: addr (0x%p), size %d", (void*)addr, size);
    return size;
}

#if defined(PKG_USING_FAL)

static int fal_flash_read_08k(long offset, rt_uint8_t *buf, size_t size);

static int fal_flash_write_08k(long offset, const rt_uint8_t *buf, size_t size);

static int fal_flash_erase_08k(long offset, size_t size);

const struct fal_flash_dev hc32_onchip_flash_08k = { "onchip_flash_08k", HC32_FLASH_START_ADRESS_08K, FLASH_SIZE_GRANULARITY_08K, (8 * 1024), {NULL, fal_flash_read_08k, fal_flash_write_08k, fal_flash_erase_08k} };

static int fal_flash_read_08k(long offset, rt_uint8_t *buf, size_t size)
{
    return hc32_flash_read(hc32_onchip_flash_08k.addr + offset, buf, size);
}

static int fal_flash_write_08k(long offset, const rt_uint8_t *buf, size_t size)
{
    return hc32_flash_write(hc32_onchip_flash_08k.addr + offset, buf, size);
}

static int fal_flash_erase_08k(long offset, size_t size)
{
    return hc32_flash_erase(hc32_onchip_flash_08k.addr + offset, size);
}

#endif
#endif /* BSP_USING_ON_CHIP_FLASH */
